﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections;
using System.IO.MemoryMappedFiles;
using System.Text;
using System.IO;

public partial class gbr
{
    public struct FilePart_Find
    {
        public FilePart_Find(string s)
        {
            iFound = 0;
            iFind = s.Length;
            baFind = new byte[iFind]; //baFound = new byte[iFind];
            for (int i = 0; i < iFind; i++)
                baFind[i] = (byte)s[i];
        }
        public bool Found(byte b)
        {
            bool bRes = false;
            if (b != baFind[iFound])
                iFound = 0;
            else if (++iFound == iFind)
            {
                bRes = true;
                iFound = 0;
            }
            return bRes;
        }
        public byte[] baFind; //, baFound;
        public int iFind, iFound;
    }
    public enum FilePart_FindState { FindStart, FindEnd };

    [Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "GetFilePart_Fill")]
    public static IEnumerator GetFilePart_Embedded(string fn, string tag)
    {
        long lBufferSize = 0x10000000000L;
        FilePart_Row row;
        FilePart_FindState state = FilePart_FindState.FindStart;
        FilePart_Find fBegin = new FilePart_Find("<" + tag + ">");
        FilePart_Find fEnd = new FilePart_Find("</" + tag + ">");
        row.pk = 1;
        row.str = new StringBuilder();
        long lFileSize = new FileInfo(fn).Length;
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fn))
        {
            for (long lFilePos = 0; lFilePos < lFileSize; lFilePos += lBufferSize)
            {
                long lBufferSize_Use = lBufferSize;
                if (lBufferSize_Use > lFilePos + lFileSize)
                    lBufferSize_Use = lFileSize - lFilePos;
                using (var accessor = mmf.CreateViewAccessor(lFilePos, lBufferSize_Use))
                {
                    byte b;
                    for (long i = 0; i < lBufferSize_Use; i++)
                    {
                        accessor.Read(i, out b);
                        if (state == FilePart_FindState.FindStart)
                        {
                            if (fBegin.Found(b))
                                state = FilePart_FindState.FindEnd;
                        }
                        else if (!fEnd.Found(b))
                            row.str.Append((char)b);
                        else
                        {
                            row.str.Remove(row.str.Length - (fEnd.iFind - 1), (fEnd.iFind - 1));
                            yield return row;
                            row.pk++;
                            row.str.Length = 0;
                            state = FilePart_FindState.FindStart;
                        }
                    }
                }
            }
        }
    }
};  